import { mount } from '../common/mount';
import type VNode from '../common/VNode';
import { patch } from './patch';

/**
 * 对比列表子节点, 更新真实DOM,首尾双指针
 * @param EL 新节点的真实 DOM
 * @param oldChildren 旧节点的子节点
 * @param newChildren 新节点的子节点
 */
export const patchListChildren = (EL: HTMLElement, oldChildren: VNode[], newChildren: VNode[]) => {
  // core: 对比列表
  // 新旧节点列表首尾指针
  let oldStartIdx = 0;
  let oldEndIdx = oldChildren.length - 1;
  let newStartIdx = 0;
  let newEndIdx = newChildren.length - 1;

  // 新列表 key 构建映射
  const keyToOldIdxMap = new Map();
  oldChildren.forEach((item, idx) => {
    // 旧列表 key 构建映射 传入新列表 key 映射 到 旧列表索引
    if (item.key !== null) {
      keyToOldIdxMap.set(item.key, idx);
    }
  });

  // 循环对比
  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
    // 获取节点
    const oldStartVNode = oldChildren[oldStartIdx] as VNode;
    const oldEndVNode = oldChildren[oldEndIdx] as VNode;
    const newStartVNode = newChildren[newStartIdx] as VNode;
    const newEndVNode = newChildren[newEndIdx] as VNode;

    // 头头对比 不移动DOM节点
    if (oldStartVNode?.key === newStartVNode?.key) {
      patch(oldStartVNode, newStartVNode, EL); // 递归对比
      oldStartIdx++;
      newStartIdx++;
      continue;
    }

    // 尾尾对比 不移动DOM节点
    if (oldEndVNode?.key === newEndVNode?.key) {
      patch(oldEndVNode, newEndVNode, EL); // 递归对比
      oldEndIdx--;
      newEndIdx--;
      continue;
    }

    // 头尾对比 DOM 移动至 oldEndIdx 后面
    if (oldStartVNode.key === newEndVNode.key) {
      patch(oldStartVNode, newEndVNode, EL); // 底部子节点优先移动
      EL.insertBefore(oldStartVNode.el as Node, (oldEndVNode.el as Node).nextSibling);
      oldStartIdx++;
      newEndIdx--;
      continue;
    }

    // 尾头对比 DOM移动至 oldStartIdx 前面
    if (oldEndVNode.key === newStartVNode.key) {
      patch(oldEndVNode, newStartVNode, EL);
      EL.insertBefore(oldEndVNode.el as Node, oldStartVNode.el as Node);
      oldEndIdx--;
      newStartIdx++;
      continue;
    }

    // 其他： 默认使用 newStartVNode.key 查找
    const oldTargetKeyIdx = keyToOldIdxMap.get(newStartVNode.key);
    if (oldTargetKeyIdx !== undefined) {
      // 找到旧节点 移动 DOM 至 oldStartIdx 前面
      const oldTargetVNode = oldChildren[oldTargetKeyIdx] as VNode;
      patch(oldTargetVNode, newStartVNode, EL);
      EL.insertBefore(oldTargetVNode.el as Node, oldStartVNode.el as Node);
    } else {
      // 未找到，创建新节点 并挂载到 oldStartIdx 前面
      mount(newStartVNode, EL, oldStartVNode.el as HTMLElement);
    }
    newStartIdx++;
  }

  // 剩余节点处理
  // 新列表有剩余节点, 插入到 oldStartIdx 前面
  while (newStartIdx <= newEndIdx) {
    mount(newChildren[newStartIdx] as VNode, EL, oldChildren[oldStartIdx]?.el as HTMLElement);
    newStartIdx++;
  }

  // 旧列表有剩余节点则删除
  while (oldStartIdx <= oldEndIdx) {
    EL.removeChild(oldChildren[oldStartIdx]?.el as Node);
    oldStartIdx++;
  }
};
